package com.golxen.framework.netty.websocket;


import com.golxen.utils.LogUtil;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.WriteBufferWaterMark;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.AttributeKey;
import org.slf4j.Logger;

public final class AgentWebSocketServer {

    private static Logger logger = LogUtil.COMMON;

    public static AttributeKey<Long> CLIENT_KEY = AttributeKey.valueOf("CLIENT_KEY");

    private long serverId;
    private int port;
    private ChannelFuture channelFuture;
    private boolean isPortOpen = true;

    EventLoopGroup bossGroup;
    EventLoopGroup workerGroup;

    public AgentWebSocketServer(long serverId, int port) {
        this.serverId = serverId;
        this.port = port;
    }

    public void start() throws Exception {
        boolean isEpoll = Epoll.isAvailable();
        int cpuNum = Runtime.getRuntime().availableProcessors();

        if (isEpoll) {
            bossGroup = new EpollEventLoopGroup(1);
            workerGroup = new EpollEventLoopGroup(cpuNum * 2 + 1);
        } else {
            bossGroup = new NioEventLoopGroup(1);
            workerGroup = new NioEventLoopGroup(cpuNum * 2 + 1);
        }

        ServerBootstrap b = new ServerBootstrap();
        b.group(bossGroup, workerGroup);
        b.channel(isEpoll ? EpollServerSocketChannel.class : NioServerSocketChannel.class);
        b.childHandler(new AgentWebSocketChannelInitializer());

        b.option(ChannelOption.SO_REUSEADDR, true);
        b.option(ChannelOption.SO_RCVBUF, 1024 * 64);
        b.option(ChannelOption.SO_BACKLOG, 1024);
        b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);

        b.childOption(ChannelOption.SO_KEEPALIVE, true);
        b.childOption(ChannelOption.SO_LINGER, 0);
        b.childOption(ChannelOption.TCP_NODELAY, true);
        b.childOption(ChannelOption.SO_SNDBUF, 1024 * 64);
        b.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);

        b.childOption(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(1024 * 64, 1024 * 128));

        LogUtil.CONSOLE.info("**** AgentServer start port 【{}】", port);

        channelFuture = b.bind(port).sync();

        logger.info("AgentWebSocketServer start port = " + port);

    }

    public boolean isPortOpen() {
        return isPortOpen;
    }

    public void stop() {
        try {
            channelFuture.channel().close().sync();

            LogUtil.CONSOLE.info("**** AgentServer stop port 【{}】", port);

            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        } catch (Exception e) {
            logger.error("Websocket close error", e);
        }
    }

}
